#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "driver/uart.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>

#include <tcp_uart.h>

#define PORT 3333

#define EX_UART_NUM UART_NUM_2
#define PATTERN_CHR_NUM (3)

#define BUF_SIZE (10240)
#define RD_BUF_SIZE (BUF_SIZE)
static QueueHandle_t uart0_queue;
// char uart_buf1[1024], uart_buf2[1024], uart_buf3[1024];
// uint16_t uart_buf1_size=0, uart_buf2_size=0, uart_buf3_size=0;

static char uart_ringbuf[11520];
static uint16_t uart_ringbuf_start=0, uart_ringbuf_stop=0;

static int g_sock;
static bool g_sockEnable = false;

static const char *TAG = "u2";

static uint8_t g_frameNum=0,g_frameTcpNum=0;


//********************* tcp 
//deal with tcp rec
static void do_retransmit(const int sock)
{
    int len;
    char rx_buffer[2048];
    g_sock = sock;
    g_sockEnable = true;

    do
    {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0)
        {
            ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
        }
        else if (len == 0)
        {
            ESP_LOGW(TAG, "Connection closed");
        }
        else
        {
            rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
            uart_write_bytes(EX_UART_NUM, (const char *)rx_buffer, len);
        }
    } while (len > 0);

    g_sockEnable = false;
}
//wait connect
static void tcp_server_task(void *pvParameters)
{
    char addr_str[128];
    int addr_family = (int)pvParameters;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    if (addr_family == AF_INET)
    {
        struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
        dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
        dest_addr_ip4->sin_family = AF_INET;
        dest_addr_ip4->sin_port = htons(PORT);
        ip_protocol = IPPROTO_IP;
    }
    else if (addr_family == AF_INET6)
    {
        bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
        dest_addr.sin6_family = AF_INET6;
        dest_addr.sin6_port = htons(PORT);
        ip_protocol = IPPROTO_IPV6;
    }

    int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
    if (listen_sock < 0)
    {
        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
        vTaskDelete(NULL);
        return;
    }
#if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
    // Note that by default IPV6 binds to both protocols, it is must be disabled
    // if both protocols used at the same time (used in CI)
    int opt = 1;
    setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
#endif

    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    if (err != 0)
    {
        ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
        goto CLEAN_UP;
    }
    ESP_LOGI(TAG, "Socket bound, port %d", PORT);

    err = listen(listen_sock, 1);
    if (err != 0)
    {
        ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
        goto CLEAN_UP;
    }

    while (1)
    {

        ESP_LOGI(TAG, "Socket listening");

        struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6
        uint addr_len = sizeof(source_addr);
        int sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len);
        if (sock < 0)
        {
            ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
            break;
        }

        // Convert ip address to string
        if (source_addr.sin6_family == PF_INET)
        {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
        }
        else if (source_addr.sin6_family == PF_INET6)
        {
            inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
        }
        ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);

        do_retransmit(sock);

        shutdown(sock, 0);
        close(sock);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

//********************* tcp ring send uart buffer
static void uart2tcp_sendring_task(void *pvParameters)
{
    vTaskDelay(4000/portTICK_PERIOD_MS);
    ESP_LOGI(TAG, "ready");
    vTaskDelay(1000/portTICK_PERIOD_MS);
    ESP_LOGI(TAG, "start");
    while (true)
    {
        if (!g_sockEnable)
        {
            uart_ringbuf_start = uart_ringbuf_stop;
            vTaskDelay(50/portTICK_PERIOD_MS);
        }
        else if (uart_ringbuf_stop - uart_ringbuf_start != 0)
        {
            int to_write = uart_ringbuf_start > uart_ringbuf_stop ? (11520 - uart_ringbuf_start) : (uart_ringbuf_stop - uart_ringbuf_start);
            // if (g_sockEnable)
            {
                if(to_write>42) to_write=42;
                g_frameTcpNum++;
                int written = send(g_sock, uart_ringbuf + uart_ringbuf_start, to_write, 0);
                if (written < 0)
                {
                    ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                }
                uart_ringbuf_start += written;
                if (uart_ringbuf_start == 11520)
                    uart_ringbuf_start = 0;
            }
        }
        else
        {
            uart_ringbuf_start = uart_ringbuf_stop;
            vTaskDelay(50/portTICK_PERIOD_MS);
        }
    }
}

//********************* uart rec
static void add_uartRingBuf(uint8_t* bufpoint, uint32_t bufsize)
{
    int emptyMemSize = uart_ringbuf_start > uart_ringbuf_stop ? (uart_ringbuf_start - uart_ringbuf_stop):(11520+uart_ringbuf_start-uart_ringbuf_stop);
    
    //剩余空间不足
    if(bufsize>=emptyMemSize)
    {
        bufsize = emptyMemSize - 1; //不能占用全部11520缓冲，必须留下一个位给stop占用以区分空/满状况
        ESP_LOGI(TAG, "over size");
    }

    //空间足够，但本帧跨越了循环
    if(bufsize>(11520-uart_ringbuf_stop))
    {
        memcpy(uart_ringbuf+uart_ringbuf_stop, bufpoint, 11520-uart_ringbuf_stop);
        memcpy(uart_ringbuf, bufpoint+11520-uart_ringbuf_stop, bufsize-(11520-uart_ringbuf_stop));
        uart_ringbuf_stop = bufsize-(11520-uart_ringbuf_stop);
        ESP_LOGI(TAG, "over period");
    }
    else{   //空间足够，本帧未跨越循环
        memcpy(uart_ringbuf+uart_ringbuf_stop, bufpoint, bufsize);
        uart_ringbuf_stop += bufsize;
        //刚好最后一个就是buf最后一位，uart_ringbuf_stop 指向最后一位的下一位，所以自动跳到第0位
        if(uart_ringbuf_stop==11520) {uart_ringbuf_stop=0;ESP_LOGI(TAG, "RS");}
    }
}

static void uart_event_task(void *pvParameters)
{
    uart_event_t event;
    size_t buffered_size;
    uint8_t *dtmp = (uint8_t *)malloc(RD_BUF_SIZE);
    for (;;)
    {
        //Waiting for UART event.
        if (xQueueReceive(uart0_queue, (void *)&event, (portTickType)portMAX_DELAY))
        {
            bzero(dtmp, RD_BUF_SIZE);
            // ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);
            switch (event.type)
            {
            //Event of UART receving data
            /*We'd better handler data event fast, there would be much more data events than
                other types of events. If we take too much time on data event, the queue might
                be full.*/
            case UART_DATA:
                // ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
                uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
                g_frameNum++;
                // ESP_LOGI(TAG, "[DATA EVT]:");
                // uart_write_bytes(EX_UART_NUM, (const char*) dtmp, event.size);

                // int to_write = event.size;
                // if (g_sockEnable)
                //     while (to_write > 0)
                //     {
                //         int written = send(g_sock, dtmp + (event.size - to_write), to_write, 0);
                //         if (written < 0)
                //         {
                //             ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                //         }
                //         to_write -= written;
                //     }

                add_uartRingBuf(dtmp, event.size);
                break;
            //Event of HW FIFO overflow detected
            case UART_FIFO_OVF:
                ESP_LOGI(TAG, "hw fifo overflow");
                // If fifo overflow happened, you should consider adding flow control for your application.
                // The ISR has already reset the rx FIFO,
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart0_queue);
                break;
            //Event of UART ring buffer full
            case UART_BUFFER_FULL:
                ESP_LOGI(TAG, "ring buffer full");
                // If buffer full happened, you should consider encreasing your buffer size
                // As an example, we directly flush the rx buffer here in order to read more data.
                uart_flush_input(EX_UART_NUM);
                xQueueReset(uart0_queue);
                break;
            //Event of UART RX break detected
            case UART_BREAK:
                ESP_LOGI(TAG, "uart rx break");
                break;
            //Event of UART parity check error
            case UART_PARITY_ERR:
                ESP_LOGI(TAG, "uart parity error");
                break;
            //Event of UART frame error
            case UART_FRAME_ERR:
                ESP_LOGI(TAG, "uart frame error");
                break;
            //UART_PATTERN_DET
            case UART_PATTERN_DET:
                uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
                int pos = uart_pattern_pop_pos(EX_UART_NUM);
                ESP_LOGI(TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
                if (pos == -1)
                {
                    // There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
                    // record the position. We should set a larger queue size.
                    // As an example, we directly flush the rx buffer here.
                    uart_flush_input(EX_UART_NUM);
                }
                else
                {
                    uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
                    uint8_t pat[PATTERN_CHR_NUM + 1];
                    memset(pat, 0, sizeof(pat));
                    uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
                    ESP_LOGI(TAG, "read data: %s", dtmp);
                    ESP_LOGI(TAG, "read pat : %s", pat);
                }
                break;
            //Others
            default:
                ESP_LOGI(TAG, "uart event type: %d", event.type);
                break;
            }
        }
    }
    free(dtmp);
    dtmp = NULL;
    vTaskDelete(NULL);
}

//********************* 
static void IRAM_ATTR uart_irq_handler(void* arg)
{
    ESP_LOGI(TAG,"1");
}

static void periodPrint (void *pvParameters)
{
    while (true)
    {
        ESP_LOGI(TAG, "->%d-%d",g_frameNum,g_frameTcpNum);
        g_frameNum=0;
        g_frameTcpNum=0;
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    
}

void uartInit()
{
        /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = 115200,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_EVEN,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    //Install UART driver, and get the queue.
    uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
    uart_param_config(EX_UART_NUM, &uart_config);

    //Set UART log level
    esp_log_level_set(TAG, ESP_LOG_INFO);
    //Set UART pins (using UART0 default pins ie no changes.)
    // uart_set_pin(EX_UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    uart_set_pin(EX_UART_NUM, 18, 19, -1, -1);

    //Set uart pattern detect function.
    uart_enable_pattern_det_baud_intr(EX_UART_NUM, '+', PATTERN_CHR_NUM, 9, 0, 0);
    //Reset the pattern queue length to record at most 20 pattern positions.
    uart_pattern_queue_reset(EX_UART_NUM, 20);

    // uart_isr_handle_t handle;
	// uart_isr_register(EX_UART_NUM, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &handle);
	// uart_enable_rx_intr(EX_UART_NUM);
    // uart_enable_intr_mask(EX_UART_NUM, 11<1);
}

void createMainTask()
{
    //Create a task to handler UART event from ISR
    xTaskCreate(uart_event_task, "uart_event_task", 20*1024, NULL, 3, NULL);
    xTaskCreate(uart2tcp_sendring_task, "uart2tcp_sendring_task", 4096, NULL, 4, NULL);
    xTaskCreate(periodPrint, "periodPrint", 4096, NULL, 0, NULL);
    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET, 2, NULL);
}